package edu.ubb.remeye.control;

import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;

import edu.ubb.remeye.client.communication.RemEyeClient;
import edu.ubb.remeye.client.settings.ClientRuntimeSettings;
import edu.ubb.remeye.communication.MessageType;
import edu.ubb.remeye.communication.message.Message;
import edu.ubb.remeye.gui.MainFrame;
import edu.ubb.remeye.gui.dialog.ErrorDialog;
import edu.ubb.remeye.gui.dialog.InformationDialog;
import edu.ubb.remeye.gui.dialog.QuestionDialog;
import edu.ubb.remeye.gui.event.EventType;
import edu.ubb.remeye.gui.event.manager.KeyEventManager;
import edu.ubb.remeye.gui.event.manager.MouseEventManager;
import edu.ubb.remeye.gui.event.manager.MouseMotionEventManager;
import edu.ubb.remeye.gui.event.manager.MouseWheelEventManager;
import edu.ubb.remeye.server.communication.RemEyeServer;

/**
 * Control class, creates a bridge between communication and GUI layers.
 * 
 * @author Andrew Ianicicau
 *
 */
public class Control {

	///////////////////////////////////////////////////////////////////////////////////
	////		METHODS - Control
	///////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * Start the server.
	 * @throws ServerStartException 
	 */
	public static void startServer() {
		RemEyeServer.getServer().startServer();
	}
	
	/**
	 * Stop the server.
	 */
	public static void stopServer() {
		RemEyeServer.getServer().stopServer();
	}
	
	/**
	 * Connect client to server.
	 */
	public static void connectClient() {
		RemEyeClient.getClient().connectClient();
	}
	
	/**
	 * Disconnect client from server.
	 */
	public static void disconnectClient() {
		RemEyeClient.getClient().disconnectClient();
	}
	
	/**
	 * Draw image to GUI.
	 * @param image Image to draw.
	 */
	public static void drawImage(BufferedImage image) {
		MainFrame.getInstance().draw(image);
	}
	
	/**
	 * Adds the event listeners.
	 */
	public static void addEventListeners() {
		MainFrame.getInstance().addKeyListener(new KeyEventManager());
		MainFrame.getInstance().getImagePanel().addMouseMotionListener(new MouseMotionEventManager());
		MainFrame.getInstance().getImagePanel().addMouseListener(new MouseEventManager());
		MainFrame.getInstance().getImagePanel().addMouseWheelListener(new MouseWheelEventManager());
	}
	
	/**
	 * Removes the event listeners.
	 */
	public static void removeEventListeners() {
		MainFrame.getInstance().removeKeyListener(MainFrame.getInstance().getKeyListeners()[0]);
		MainFrame.getInstance().getImagePanel().removeMouseMotionListener(MainFrame.getInstance().getImagePanel().getMouseMotionListeners()[0]);
		MainFrame.getInstance().getImagePanel().removeMouseListener(MainFrame.getInstance().getImagePanel().getMouseListeners()[0]);
		MainFrame.getInstance().getImagePanel().removeMouseWheelListener(MainFrame.getInstance().getImagePanel().getMouseWheelListeners()[0]);
	}
	
	/**
	 * Send a key or mouse event.
	 * @param eventType Event type to send.
	 * @param inputEvent Event data.
	 */
	public static void sendEvent(EventType eventType, InputEvent inputEvent) {
		byte[] eventData = getEventData(eventType, inputEvent);
		getBytesFromInteger(eventType.ordinal(), 0, eventData);
		Message eventMessage = new Message(MessageType.EVENT, eventData);
		ClientRuntimeSettings.getInstance().getClientSession().write(eventMessage);
	}
	
	/**
	 * Show a question dialog with yes/no answers to user.
	 * @param question Question string.
	 * @return True if yes was selected, else false.
	 */
	public static boolean getAnswer(String question) {
		QuestionDialog questionDialog = new QuestionDialog(question);
		return questionDialog.showDialog();
	}
	
	/**
	 * Show error dialog.
	 * @param errorMessage Error message to show.
	 */
	public static void showError(String errorMessage) {
		ErrorDialog errorDialog = new ErrorDialog(errorMessage);
		errorDialog.showDialog();
	}
	
	/**
	 * Show information dialog.
	 * @param informationMessage Information message to show.
	 */
	public static void showInformation(String informationMessage) {
		InformationDialog informationDialog = new InformationDialog(informationMessage);
		informationDialog.showDialog();
	}
	
	/**
	 * Puts the bytes of an integer into a byte array from the given index.
	 * @param value Integer to extract bytes.
	 * @param index Index to put bytes in the byte array.
	 * @param array Array to put the extracted bytes.
	 */
	private static void getBytesFromInteger(int value, int index, byte[] array) {
		array[index] =  (byte)(value >>> 24);
		array[index+1] =  (byte)(value >>> 16);
		array[index+2] =  (byte)(value >>> 8);
		array[index+3] =  (byte)(value);
	}
	
	/**
	 * Returns the event data specific to every event type.
	 * @param eventType Type of event.
	 * @param event Event object containing information about the event.
	 * @return Byte array with necessary event data specific to event.
	 */
	private static byte[] getEventData(EventType eventType, InputEvent event) {
		byte[] data = null;
		
		if(eventType.equals(EventType.KEY_PRESSED) || eventType.equals(EventType.KEY_RELEASED)) {
			data = new byte[8];
			getBytesFromInteger(((KeyEvent)event).getKeyCode(), 4, data);
		}
		
		if(eventType.equals(EventType.MOUSE_MOVED)) {
			data = new byte[12];
			getBytesFromInteger(((MouseEvent)event).getX(), 4, data);
			getBytesFromInteger(((MouseEvent)event).getY(), 8, data);
		}
		
		if(eventType.equals(EventType.MOUSE_CLICKED) || eventType.equals(EventType.MOUSE_DOUBLE_CLICKED)) {
			data = new byte[16];
			getBytesFromInteger(((MouseEvent)event).getButton(), 4, data);
			getBytesFromInteger(((MouseEvent)event).getX(), 8, data);
			getBytesFromInteger(((MouseEvent)event).getY(), 12, data);
		}
		
		if(eventType.equals(EventType.MOUSE_PRESSED) || eventType.equals(EventType.MOUSE_RELEASED)) {
			data = new byte[16];
			getBytesFromInteger(((MouseEvent)event).getButton(), 4, data);
			getBytesFromInteger(((MouseEvent)event).getX(), 8, data);
			getBytesFromInteger(((MouseEvent)event).getY(), 12, data);
		}
		
		if(eventType.equals(EventType.MOUSE_DRAGGED)) {
			data = new byte[4];
		}
		
		if(eventType.equals(EventType.MOUSE_WHEEL)) {
			data = new byte[8];
			getBytesFromInteger(((MouseWheelEvent)event).getWheelRotation(), 4, data);
		}
		
		return data;
	}

}
